import { authStore } from "@/store";
import axios from "axios";

const baseURL = import.meta.env.VITE_BASE_URI;

/**
 * SSE 客户端 (基于 Fetch API)
 * 支持自定义请求头、自动重连、心跳检测等功能
 */
class SSE<T = string> {
    private url: string;// SSE 服务端 URL
    private options: SSEOptions<T>;// 配置选项
    private controller: AbortController | null = null;// 控制器
    private reconnectTimer: number | null = null;// 重新连接定时器
    private heartbeatTimer: number | null = null;// 心跳定时器
    private lastMessageTime: number = 0;// 上次收到消息的时间
    private isConnected: boolean = false;// 是否已连接
    private isDisconnecting: boolean = false;// 是否正在断开连接
    private isRefreshingToken = false;// 是否正在刷新 token
    private reconnectAttempts: number = 0;// 重新连接尝试次数
    private maxReconnectAttempts: number = 5;// 最大重新连接尝试次数
    private isTokenRefreshReconnect: boolean = false; // 标记是否是 token 刷新后的重连

    /**
     * 构造函数
     * @param url SSE 服务端 URL
     * @param options 配置选项
     */
    constructor(url: string, options: SSEOptions<T> = {}) {
        this.url = this.handerUrl(url, options.params);// 处理 URL 和参数
        this.options = this.handleOptions(options);// 处理请求信息，包括请求头，请求体等
        this.connect();
    }

    /**
     * 处理 URL
     * @param url
     * @returns
     */
    private handerUrl(url: string, params?: Record<string, any>): string {
        if (!url.startsWith("/")) {
            url = "/" + url;
        }
        let query = "";
        if (params && Object.keys(params).length > 0) {
            query = "?" + new URLSearchParams(params).toString();
        }
        return baseURL + url + query;
    }

    /**
     * 添加请求信息
     * @param options
     * @returns
     */
    private handleOptions(options: SSEOptions<T>): SSEOptions<T> {
        const store = authStore();

        // 请求头
        const headers: HeadersInit = { 'Accept': 'text/event-stream' };

        // 添加 token
        if (store.accessToken) {
            headers['Authorization'] = store.accessToken;
        }

        // 合并自定义头部
        if (options.headers) {
            Object.assign(headers, options.headers);
        }

        // 合并请求体
        if (options.body) {
            options.body = JSON.stringify(options.body) as BodyInit;
            headers['Content-Type'] = 'application/json';
            options.method = 'POST';
        }

        // 请求方式
        options.method = options.method || "GET";

        // 设置默认值并返回配置
        return {
            credentials: 'include',// 请求凭证
            cache: 'no-cache',// 缓存模式
            mode: 'cors',// 跨域模式
            redirect: 'follow',// 重定向
            referrerPolicy: 'origin-when-cross-origin',// 来源策略
            keepalive: true,// 保持连接
            autoReconnect: true,// 是否自动重连
            headers: headers,// 请求头
            ...options
        } as SSEOptions<T>;
    }

    /**
     * 建立 SSE 连接
     * @private
     */
    private async connect(): Promise<void> {
        // 清理之前的连接
        this.disconnect();

        try {
            // 创建控制器，用于断开连接
            this.controller = new AbortController();
            this.options.signal = this.controller.signal;
            // 发起 Fetch 请求
            const response = await fetch(this.url, this.options);

            // 检查响应状态
            if (!response.ok) {
                throw new Error(response.statusText || "SSE连接失败");
            }

            // 检查内容类型
            const contentType = response.headers.get("content-type");
            if (!contentType?.includes("text/event-stream")) {
                throw new Error("无效的SSE响应类型");
            }

            this.isConnected = true;// 标记已连接
            this.lastMessageTime = Date.now();// 初始化最后消息时间
            this.isDisconnecting = false;// 重置断开标记

            // 触发连接打开回调
            this.options.onOpen?.();

            // 设置心跳检测
            this.setupHeartbeatCheck();

            // 获取可读流
            const reader = response.body?.getReader();
            if (!reader) {
                throw new Error("无法获取SSE流");
            }

            // 处理流数据
            await this.processStream(reader);
        } catch (error) {
            this.handleError(error as Error);
        }
    }

    /**
     * 处理 SSE 数据流
     * @private
     */
    private async processStream(reader: ReadableStreamDefaultReader<Uint8Array>): Promise<void> {
        const decoder = new TextDecoder();
        let buffer = "";

        try {
            while (true) {
                const { done, value } = await reader.read();
                if (done) {
                    this.handleDisconnect();
                    return;
                }

                buffer += decoder.decode(value, { stream: true });
                this.lastMessageTime = Date.now();

                // 按行处理事件流
                const lines = buffer.split("\n");
                buffer = lines.pop() || "";

                for (const line of lines) {
                    if (line && line.trim().startsWith("data:")) {
                        const data = line.replace(/^data:\s*/, "").trim();
                        if (data) {
                            await this.processEventLine(data);
                        }
                    }
                }
            }
        } catch (error) {
            this.handleError(error as Error);
        }
    }

    /**
     * 处理单行 SSE 事件
     * @private
     */
    private async processEventLine(data: string): Promise<void> {
        try {
            // 尝试解析 JSON 数据
            const json = JSON.parse(data);

            // 处理 4001 错误
            if (json?.code === 4001 && json.success === false) {
                console.warn("收到 4001 错误，触发 token 刷新");
                await this.handleTokenExpired();
                return;
            }

            // 正常消息处理
            if (json.success) {
                this.options.onMessage?.(json as T);
                this.reconnectAttempts = 0; // 收到正常消息时重置重连次数
            } else {
                // 其他错误类型的消息
                this.options.onMessage?.(json as T);
            }

        } catch {
            // 如果不是 JSON，直接传递原始字符串
            this.options.onMessage?.(data as unknown as T);
        }
    }

    /**
     * 刷新 accessToken
     * @private
     */
    private async refreshAccessToken(): Promise<boolean> {
        // 防止重复刷新
        if (this.isRefreshingToken) {
            return false;
        }

        const store = authStore();
        const refreshToken = store.refreshToken;

        if (!refreshToken) {
            return false;
        }

        try {
            this.isRefreshingToken = true;
            const response = await axios.post(`${baseURL}/refresh`, {}, { withCredentials: true, headers: { "X-Refresh-Token": refreshToken } });
            const accessToken = response?.headers["authorization"];
            if (accessToken) {
                // 保存token到状态库
                store.setAccessToken(accessToken)
                return true;
            } else {
                throw new Error('刷新 token 响应格式错误');
            }
        } catch (error: any | null) {
            this.isRefreshingToken = false;
            // 触发错误回调
            this.options.onError?.(new Error(error?.message));
            return false;
        } finally {
            this.isRefreshingToken = false;
        }
    }

    /**
     * 处理 token 过期
     * @private
     */
    private async handleTokenExpired(): Promise<void> {
        // 先断开当前连接
        this.disconnect();

        // 刷新 token
        const refreshSuccess = await this.refreshAccessToken();

        if (refreshSuccess) {
            this.isConnected = false;
        } else {
            console.error('Token 刷新失败，停止自动重连');
            // 停止自动重连，避免无限循环
            this.options.autoReconnect = false;
        }
    }

    /**
     * 设置心跳检测
     * @private
     */
    private setupHeartbeatCheck(): void {
        if (this.heartbeatTimer) {
            clearInterval(this.heartbeatTimer);
        }

        this.heartbeatTimer = window.setInterval(() => {
            const timeSinceLastMessage = Date.now() - this.lastMessageTime;
            if (timeSinceLastMessage > (this.options.heartbeatTimeout || 30000)) {
                this.handleError(new Error("SSE心跳超时"));
            }
        }, 15000); // 每15秒检查一次
    }

    /**
     * 处理连接断开
     * @private
     */
    private handleDisconnect(): void {
        this.isConnected = false;
        this.cleanup();

        if (this.options.autoReconnect && !this.isTokenRefreshReconnect) {
            this.scheduleReconnect();
        }
    }

    /**
     * 处理错误
     * @private
     */
    private handleError(error: Error): void {
        this.isConnected = false;
        this.cleanup();
        this.options.onError?.(error);
        if (this.options.autoReconnect && !this.isDisconnecting) {
            // 触发重连
            this.scheduleReconnect();
        }
    }

    /**
     * 安排重新连接
     * @private
     */
    private scheduleReconnect(): void {
        if (this.reconnectTimer) {
            clearTimeout(this.reconnectTimer);
        }

        // 检查重连次数限制
        if (this.reconnectAttempts >= this.maxReconnectAttempts) {
            console.warn("SSE重连次数已达上限，停止重连");
            this.options.onError?.(new Error('SSE重连次数已达上限'));
            return;
        }

        // 重连次数加一
        this.reconnectAttempts++;

        // 指数退避策略
        const delay = Math.min((this.options.reconnectInterval || 3000) * Math.pow(1.5, this.reconnectAttempts - 1), 30000);
        console.log(`第${this.reconnectAttempts}次尝试重连， 延迟${delay}毫秒...`);
        this.reconnectTimer = window.setTimeout(() => this.connect(), delay);
    }

    /**
     * 清理资源
     * @private
     */
    private cleanup(): void {
        this.controller?.abort();
        this.controller = null;

        if (this.heartbeatTimer) {
            clearInterval(this.heartbeatTimer);
            this.heartbeatTimer = null;
        }
    }

    /**
     * 主动断开连接
     */
    public disconnect(): void {
        if (this.isDisconnecting) return;
        this.isDisconnecting = true;

        this.cleanup();

        if (this.reconnectTimer) {
            clearTimeout(this.reconnectTimer);
            this.reconnectTimer = null;
        }

        this.isConnected = false;
        this.isTokenRefreshReconnect = false;
        this.isDisconnecting = false;
    }

    /**
     * 获取当前连接状态
     */
    public getConnectionStatus(): boolean {
        return this.isConnected;
    }

    /**
     * 获取重连次数
     */
    public getReconnectAttempts(): number {
        return this.reconnectAttempts;
    }
}

/**
 * 通用SSE请求
 * @param url 请求路径
 * @param onMessage 接收到消息时的回调函数
 * @returns 
 */
const request = (url: string, onMessage: SSEMessageCallback<any>) => {
    return new SSE(url, { onMessage });
}

/**
 * 暴露
 */
export {
    request,
    SSE,
};